using System;
using System.Collections.Generic;
using Gameplay.PVE.Data;
using Gameplay.PVE.Utils;
using UnityEditor;
using UnityEngine;

namespace Gameplay.PVE.Editor
{
    public class RpgLevelEditorWindow : EditorWindow
    {
        public static RpgLevelData data;
        public static RpgLevelEditorWindow Instance;

        public RpgLevelEditorWindow()
        {
            Instance = this;
        }
        [MenuItem("GTATools/GamePlay/PVE/RpgLevelEditor")]
        public static void Init()
        {
            var window = EditorWindow.GetWindow<RpgLevelEditorWindow>("rpgA编辑器", true);
        }
        
        private Vector2 scrollPos;

        private string currentLevel = "0";
        private string currentStage = "0";
        private string currentStageType = "0";
        private string currentStageParam = "0";
        private string currentStagePreviousStage = "0";
        private int currentStageId;
        private int currentLevelId;
        private int currentStageTypeId;
        private float currentStageParamValue;
        private int currentStagePreviousStageId;
        
        private List<int> stageType = new List<int>();
        private List<float> stageParam = new List<float>();
        private List<int> stagePrevious = new List<int>();
        private string obstacleType = "100";


        private Dictionary<int,Transform> stageParents = new Dictionary<int,Transform>();
        private List<GameObject> myBirthPoints = new List<GameObject>();
        private List<GameObject> enemyBirthPoints = new List<GameObject>();
        private List<GameObject> attackPoints = new List<GameObject>();

        private void CreateMyBirthPoint(Vector3 position)
        {
            CheckStage();
            CheckRemove(ref myBirthPoints);
            var obj = GameObject.CreatePrimitive(PrimitiveType.Capsule);
            obj.transform.position = position;
            obj.GetComponent<Renderer>().material.color = Color.blue;
            obj.name = string.Format("MyBirth{0}_{1}", currentStageId, myBirthPoints.Count);
            obj.transform.parent = stageParents[currentStageId];
            Selection.activeGameObject = obj;
            myBirthPoints.Add(obj);
        }

        private void CreateEnemyBirthPoint(int id, Vector3 position, int spawnType = 0,float spawnParam = 0)
        {
            CheckStage();
            CheckRemove(ref enemyBirthPoints);
            var obj = new GameObject(string.Format("EnemyBirth{0}_{1}", currentStageId, enemyBirthPoints.Count));
            obj.transform.position = position;
            obj.transform.parent = stageParents[currentStageId];
            var enemyAttribute = obj.AddComponent<EnemyAttributes>();
            enemyAttribute.id = id;
            enemyAttribute.stageId = currentStageId;
            enemyAttribute.spawnType = spawnType;
            enemyAttribute.spawnParam = spawnParam;
            enemyAttribute.UpdateModel();
            Selection.activeGameObject = obj;
            enemyBirthPoints.Add(obj);
        }
        
        private void CreateObstacle(int id,Vector3 position,Vector3 rotation,Vector3 scale,int width,int height,List<Vector3> playerHidePoints = null,List<Vector3> enemyHidePoints = null,int breakId = 0,int spawnType = 0,float spawnParam = 0)
        {
            if (width == 0)
            {
                width = 3;
            }

            if (height == 0)
            {
                height = 3;
            }
            CheckStage();
            var obj = GetObstacleById(id);
            obj.transform.localScale = scale;
            obj.transform.position = position;
            obj.transform.localEulerAngles = rotation;
            obj.name = string.Format("Obstacle{0}_{1}", currentStageId, id);
            obj.transform.parent = stageParents[currentStageId];
            var obstacleAttribute = obj.AddComponent<ObstacleAttributes>();
            if (playerHidePoints != null)
            {
                for (int i = 0; i < playerHidePoints.Count; i++)
                {
                    var point = CreateHidePoint(obj,playerHidePoints[i],0);
                    obstacleAttribute.playerHidePoints.Add(point);
                }
            }
            if (enemyHidePoints != null)
            {
                for (int i = 0; i < enemyHidePoints.Count; i++)
                {
                    var point = CreateHidePoint(obj,enemyHidePoints[i],2);
                    obstacleAttribute.enemyHidePoints.Add(point);
                }
            }

            obstacleAttribute.stage = currentStageId;
            obstacleAttribute.hp = 5;
            obstacleAttribute.id = id;
            obstacleAttribute.width = width;
            obstacleAttribute.height = height;
            obstacleAttribute.breakId = breakId;
            obstacleAttribute.spawnType = spawnType;
            obstacleAttribute.spawnParam = spawnParam;
            
            Selection.activeGameObject = obj;
            UpdateBlockPoints(obj.transform,width,height);
        }

        private void CreateAttackPoints(Vector3 position)
        {
            return;
            CheckStage();
            CheckRemove(ref attackPoints);
            var obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
            obj.transform.localScale = new Vector3(1,1,1);
            obj.GetComponent<Renderer>().material.color = Color.yellow;
            obj.name = string.Format("AttackPoint");
            obj.transform.parent = stageParents[currentStageId];
            obj.transform.position = position;
            Selection.activeGameObject = obj;
            attackPoints.Add(obj);
        }

        private GameObject CreateHidePoint(GameObject parent, Vector3 position, int team, bool addToList = false)
        {
            var obstacleAttribute = parent.GetComponent<ObstacleAttributes>();
            if (obstacleAttribute == null)
            {
                return null;
            }
            var obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
            obj.transform.localScale = new Vector3(1,1,1);
            obj.GetComponent<Renderer>().material.color = team == 0 ? Color.white : Color.black;
            obj.name = string.Format("HidePoint");
            obj.transform.parent = parent.transform;
            obj.transform.position = position;

            if (addToList)
            {
                if (team == 0)
                {
                    obstacleAttribute.playerHidePoints.Add(obj);
                }
                else
                {
                    obstacleAttribute.enemyHidePoints.Add(obj);
                }
            }
           
            Selection.activeGameObject = obj;
            return obj;
        }

        private GameObject GetObstacleById(int id)
        {
#if UNITY_EDITOR
            var path = PveResourceManager.GetHeroPath(id) + ".prefab";
            var obj = AssetDatabase.LoadAssetAtPath<GameObject>(path);
            if (obj == null)
            {
                return new GameObject();
            }
            return Instantiate(obj);
#endif
        }

        private void UpdateAllBlockPoints()
        {
            var obstaclesInScene = FindObjectsOfType<ObstacleAttributes>();
            for (int i = 0; i < obstaclesInScene.Length; i++)
            {
                var obj = obstaclesInScene[i];
                UpdateBlockPoints(obj.transform,obj.width,obj.height);
            }
            
        }
        
        private void UpdateBlockPoints(Transform parent,int width,int height)
        {
                
            for (int i = 0; i < parent.childCount; i++)
            {
                var child = parent.GetChild(i);
                if (child.gameObject.name.Contains("Astar"))
                {
                    GameObject.DestroyImmediate(child.gameObject);
                    i--;
                }
            }
            
            
            Vector3[] corners = new Vector3[4];
            float gridW = AStarMgr.gridSize.x;
            float gridH = AStarMgr.gridSize.y;
            
            corners[0] = new Vector3(-width / 2f * gridW,0,-height / 2f * gridH);
            corners[1] = new Vector3(-width / 2f * gridW,0,height / 2f * gridH);
            corners[2] = new Vector3(width / 2f * gridW,0,height / 2f * gridH);
            corners[3] = new Vector3(width / 2f * gridW,0,-height / 2f * gridH);

            float parentY = parent.localEulerAngles.y;
            for (int i = 0; i < corners.Length; i++)
            {
                corners[i] = Quaternion.AngleAxis(parentY, Vector3.up) * corners[i];
            }
            
            var position = parent.position;
            int halfWidth = Mathf.CeilToInt(width / 2f);
            int halfHeight = Mathf.CeilToInt(height / 2f);
            float midX = Mathf.RoundToInt(position.x / gridW) * gridW;
            float midZ = Mathf.RoundToInt(position.z / gridH) * gridH;
            int region = halfWidth + halfHeight;
            
            Vector2[] cornersV2 = new Vector2[4];
            for (int i = 0; i < 4; i++)
            {
                cornersV2[i] = new Vector2(corners[i].x,corners[i].z);
            }
            for (float i = -region; i <= region; i ++)
            {
                for (float j = -region; j <= region; j ++)
                {
                    bool isBlock = true;
                    var blockPos = new Vector2(i * gridW,j * gridH);
                    for (int k = 0; k < 4; k++)
                    {
                        var cornerStart = cornersV2[k % 4];
                        var cornerEnd = cornersV2[(k + 1) % 4];
                        if (PveUtils.GetIntersection(cornerStart, cornerEnd, Vector2.zero, blockPos) != Vector2.zero)
                        {
                            isBlock = false;
                            break;
                        }
                    }

                    if (isBlock)
                    {
                        var block = GameObject.CreatePrimitive(PrimitiveType.Plane);
                        block.name = "AstarBlock";
                        block.transform.localEulerAngles = Vector3.zero;
                        block.transform.localScale = new Vector3(gridW,1,gridH) / 10f *0.9f;
                        block.transform.parent = parent;
                        block.transform.position = new Vector3(i * gridW + midX,0.01f,j * gridH + midZ);
                    }
                }
            }
        }
        
        
        private void CheckRemove(ref List<GameObject> list)
        {
            for (int i = 0; i < list.Count; i++)
            {
                if (!list[i])
                {
                    list.RemoveAt(i);
                    i--;
                }
            }
        }

        private void CheckStage()
        {
            if (!stageParents.ContainsKey(currentStageId))
            {
                var stage = new GameObject("Stage" + currentStageId);
                stageParents.Add(currentStageId,stage.transform);
                int count =  currentStageId - stageType.Count;
                for (int i = 0; i <= count; i++)
                {
                    stageType.Add(0);
                    stageParam.Add(0);
                    stagePrevious.Add(0);
                }
            }
        }

        private void ClearScene()
        {
            currentStage = "0";
            currentStageId = 0;

            stageParam.Clear();
            stageType.Clear();
            stagePrevious.Clear();
            obstacleType = "100";

            foreach (var item in stageParents.Values)
            {
                GameObject.DestroyImmediate(item.gameObject);
            }
            stageParents.Clear();
            myBirthPoints.Clear();
            enemyBirthPoints.Clear();
            attackPoints.Clear();
        }
        

        private Vector3 GetDefaultPosition()
        {
            var obj = Selection.activeGameObject;
            if (obj != null)
            {
                return obj.transform.position;
            }

            return Vector3.zero;
        }

        private void OnDestroy()
        {
            ClearScene();
        }

        private void SaveStageParam()
        {
            int.TryParse(currentStageType, out currentStageTypeId);
            float.TryParse(currentStageParam, out currentStageParamValue);
            stageParam[currentStageId] = currentStageParamValue;
            stageType[currentStageId] = currentStageTypeId;
            stagePrevious[currentStageId] = currentStagePreviousStageId;
        }
        
        private void OnGUI()
        {
            EditorGUILayout.BeginHorizontal();
            scrollPos =
                EditorGUILayout.BeginScrollView(scrollPos, false, false);
            EditorGUILayout.Space();
            
            EditorGUILayout.BeginHorizontal();
            currentLevel = EditorGUILayout.TextField(currentLevel);
            if (GUILayout.Button("加载地图配置"))
            {
                int.TryParse(currentLevel, out currentLevelId);
                ClearScene();
                LoadMapData(currentLevelId);
            }
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.Space();

            if (data == null)
            {
                EditorGUILayout.EndScrollView();
                EditorGUILayout.EndHorizontal();
                return;
            }
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("当前阶段:");
            currentStage = EditorGUILayout.TextField(currentStage);
            
            if (GUILayout.Button("修改阶段"))
            {
                int.TryParse(currentStage, out currentStageId);
                CheckStage();
                currentStageParam = stageParam[currentStageId].ToString();
                currentStageType = stageType[currentStageId].ToString();
                currentStagePreviousStage = stagePrevious[currentStageId].ToString();
            }
            EditorGUILayout.EndHorizontal();
            
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("阶段类型: (0普通  1boss  2按时间刷新)");
            currentStageType = EditorGUILayout.TextField(currentStageType);
            EditorGUILayout.EndHorizontal();
            
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("阶段参数");
            currentStageParam = EditorGUILayout.TextField(currentStageParam);
            EditorGUILayout.EndHorizontal();
            
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("阶段前置阶段");
            currentStagePreviousStage = EditorGUILayout.TextField(currentStagePreviousStage);
            EditorGUILayout.EndHorizontal();
            if (GUILayout.Button("保存阶段类型和参数"))
            {
                SaveStageParam();
            }
            
            if (GUILayout.Button("我方出生点"))
            {
                CreateMyBirthPoint(GetDefaultPosition());
            }
            if (GUILayout.Button("敌方出生点"))
            {
                CreateEnemyBirthPoint(10,GetDefaultPosition());
            }
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("障碍物类型:");
            obstacleType = EditorGUILayout.TextField(obstacleType);
            if (GUILayout.Button("添加障碍"))
            {
                int type;
                int.TryParse(obstacleType, out type);
                CreateObstacle(type,GetDefaultPosition(),Vector3.zero,new Vector3(1,1,1),3,3);
            }
            EditorGUILayout.EndHorizontal();

            if (GUILayout.Button("添加我方隐藏点"))
            {
                CreateHidePoint(Selection.activeGameObject,GetDefaultPosition(),0,true);
            }
            if (GUILayout.Button("添加敌方隐藏点"))
            {
                CreateHidePoint(Selection.activeGameObject,GetDefaultPosition(),2,true);
            }

            
            if (GUILayout.Button("添加攻击点"))
            {
                CreateAttackPoints(GetDefaultPosition());
            }

            if (GUILayout.Button("刷新阻挡点"))
            {
                UpdateAllBlockPoints();
            }

            if (GUILayout.Button("保存"))
            {
                try
                {
                    Save();
                }
                catch (Exception e)
                {
                    EditorUtility.DisplayDialog("通知", "保存失败，我们之中有且只有一个憨批", "OK");
                    throw;
                }
                
                EditorUtility.SetDirty(data);
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();
                EditorUtility.SetDirty(data);
                EditorUtility.DisplayDialog("通知", "保存成功", "OK");

            }
            EditorGUILayout.EndScrollView();
            EditorGUILayout.EndHorizontal();
        }

        private void Save()
        {
            SaveStageParam();
            CheckRemove(ref enemyBirthPoints);
            CheckRemove(ref myBirthPoints);
            //CheckRemove(ref obstacles);
            var obstaclesInScene = FindObjectsOfType<ObstacleAttributes>();
            var enemyInScene = FindObjectsOfType<EnemyAttributes>();
            List<RpgLevelData.RpgStage> list = new List<RpgLevelData.RpgStage>();
            foreach (var item in stageParents)
            {
                var enemyPositions = new List<Vector3>();
                var enemyIds = new List<int>();
                var enemySpawnParam = new List<float>();
                var enemySpawnType = new List<int>();
                for (int i = 0; i < enemyInScene.Length; i++)
                {
                    var enemyAttribute = enemyInScene[i];
                    var stage = enemyAttribute.stageId;
                    if (stage == item.Key)
                    {
                        enemyPositions.Add(enemyAttribute.transform.position);
                        enemyIds.Add(enemyAttribute.id);
                        enemySpawnParam.Add(enemyAttribute.spawnParam);
                        enemySpawnType.Add(enemyAttribute.spawnType);
                    }
                }

                var myPositions = new List<Vector3>();
                for (int i = 0; i < myBirthPoints.Count; i++)
                {
                    if (myBirthPoints[i] == null)
                    {
                        continue;
                    }
                    var n = myBirthPoints[i].name.Split('_')[0];
                    var stage = n[n.Length - 1].ToString();
                    if (stage == item.Key.ToString())
                    {
                        myPositions.Add(myBirthPoints[i].transform.position);
                    }
                }

                var obstacles = new List<RpgLevelData.RpgObstacle>();
                for (int i = 0; i < obstaclesInScene.Length; i++)
                {
                    /*var temp = obstaclesInScene[i].name.Split('_');
                    var n = temp[0];
                    var obstacleId = temp[1];
                    int.TryParse(obstacleId, out int oId);*/
                    var attribute = obstaclesInScene[i].GetComponent<ObstacleAttributes>();
                    var obstacleId = attribute.id;
                    var stage = attribute.stage;
                    if (stage == item.Key)
                    {
                        var playerPoints = new List<Vector3>();
                        var enemyPoints = new List<Vector3>();
                        for (int j = 0; j < attribute.playerHidePoints.Count; j++)
                        {
                            if (attribute.playerHidePoints[j] == null)
                            {
                                continue;
                            }
                            playerPoints.Add(attribute.playerHidePoints[j].transform.position);
                        }

                        for (int j = 0; j < attribute.enemyHidePoints.Count; j++)
                        {
                            if (attribute.enemyHidePoints[j] == null)
                            {
                                continue;
                            }
                            enemyPoints.Add(attribute.enemyHidePoints[j].transform.position);
                        }
                        
                        obstacles.Add(new RpgLevelData.RpgObstacle
                        {
                            id = i,
                            hp = attribute.hp,
                            playerHidePoints = playerPoints,
                            enemyHidePoints = enemyPoints,
                            obstacleId = obstacleId,
                            position = obstaclesInScene[i].transform.position,
                            rotation = obstaclesInScene[i].transform.localEulerAngles,
                            scale = obstaclesInScene[i].transform.localScale,
                            height = attribute.height,
                            width = attribute.width,
                            breakId = attribute.breakId,
                            spawnParam = attribute.spawnParam,
                            spawnType = attribute.spawnType,
                        });
                    }
                }

                var attackPoints = new List<Vector3>();
                for (int i = 0; i < this.attackPoints.Count; i++)
                {
                    if (this.attackPoints[i] == null)
                    {
                        continue;
                    }
                    var parentName = this.attackPoints[i].transform.parent.name;
                    var stage = parentName[parentName.Length - 1];
                    if (stage.ToString() == item.Key.ToString())
                    {
                         attackPoints.Add(this.attackPoints[i].transform.position);
                    }
                }
                
                list.Add(new RpgLevelData.RpgStage
                {
                    stageType = stageType[item.Key],
                    stageParam = stageParam[item.Key],
                    stageId = item.Key,
                    previousStageId = stagePrevious[item.Key],
                    enemyBirthPositions = enemyPositions,
                    enemySpawnParam = enemySpawnParam,
                    enemySpawnType = enemySpawnType,
                    enemyIds = enemyIds,
                    playerBirthPositions = myPositions,
                    obstacles = obstacles,
                    attackPositions = attackPoints,
                });
            }
            data.stages = list;
        }
        
        private void LoadMapData(int index)
        {
            string path = string.Format("Assets/ResourcesAssets/Pve/Data/level{0}.asset",index);
            data = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path) as RpgLevelData;
            if (data == null)
            {
                data = ScriptableObject.CreateInstance<RpgLevelData>();
                data.Init();
                AssetDatabase.CreateAsset(data, path);
            }

            stageParam.Clear();
            stageType.Clear();
            stagePrevious.Clear();
            for (int i = 0; i < data.stages.Count; i++)
            {
                currentStageId = i;
                var stage = data.stages[i];
                stageType.Add(stage.stageType);
                stageParam.Add(stage.stageParam);
                stagePrevious.Add(stage.previousStageId);
                
                for (int j = 0; stage.playerBirthPositions != null && j < stage.playerBirthPositions.Count; j++)
                {
                    CreateMyBirthPoint(stage.playerBirthPositions[j]);
                }
                for (int j = 0; stage.enemyIds != null && j < stage.enemyIds.Count; j++)
                {
                    int type = 0;
                    if (stage.enemySpawnType != null && stage.enemySpawnType.Count > j)
                    {
                        type = stage.enemySpawnType[j];
                    }

                    float param = 0;
                    if (stage.enemySpawnParam != null && stage.enemySpawnParam.Count > j)
                    {
                        param = stage.enemySpawnParam[j];
                    }
                    CreateEnemyBirthPoint(stage.enemyIds[j],stage.enemyBirthPositions[j],type,param);
                }
                for (int j = 0; stage.obstacles != null && j < stage.obstacles.Count; j++)
                {
                    var obstacle = stage.obstacles[j];
                    CreateObstacle(obstacle.obstacleId,obstacle.position,obstacle.rotation,obstacle.scale,obstacle.width,obstacle.height,obstacle.playerHidePoints,obstacle.enemyHidePoints,obstacle.breakId,obstacle.spawnType,obstacle.spawnParam);
                }

                for (int j = 0; stage.attackPositions != null && j < stage.attackPositions.Count; j++)
                {
                    CreateAttackPoints(stage.attackPositions[j]);
                }
            }

            currentStageId = 0;
        }
    }
}